package com.token.sysflow.hander;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.token.base.JsonGenerator;
import com.token.base.Pl;
import com.token.sql.IntegrateSql;
import com.token.sql.TableBeanPrepareSql;
import com.token.sysflow.bean.*;
import com.token.sysflow.enumerate.FlowStatusEnum;
import com.token.sysflow.enumerate.HSWayEnum;
import com.token.sysflow.exception.ErrorCodeEnum;
import com.token.sysflow.exception.FlowException;
import com.token.sysflow.impl.SubmitFlowImpl;
import lombok.Getter;
import lombok.Setter;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.*;

/**
 * 审批数据
 *
 * @author chenjianxiong 2020/4/21
 */
public class ApproveProcess extends FSysApproveProcess {
    private final TableBeanPrepareSql vthSql;
    private TableBeanPrepareSql tSql;
    private final FSysApprovingVth vth;
    private Step curStep;


    /**
     * 流程的审批数据,
     */
    @Getter
    private final JSONArray flowProcessInfo;
    /**
     * 暂停 用于组织会审时，不通过时退回取办理人
     */
    public static int STATUS_PAUSE = 2;
    public static int STATUS_END = 1;
    public static int STATUS_IND = 0;

    /**
     * 停止
     */
    public static int STATUS_STOP = 4;
    public static int HSRESULT_NO = 0;
    public static int HSRESULT_YES = 1;
    public static int HSRESULT_ING = 2;

    /**
     * 当前是否是退回操作
     */
    @Setter
    @Getter
    private boolean returnAction = false;
    @Getter
    private boolean isHas = false;

    /**
     * @param iSql     类库
     * @param keyValue 关联Id
     * @throws Exception 抛出错误
     */
    public ApproveProcess(IntegrateSql iSql, String keyValue) throws Exception {
        super(iSql, keyValue);
        vth = new FSysApprovingVth(iSql);
        vthSql = new TableBeanPrepareSql(vth);
        this.isHas = this.isInitRs();
        flowProcessInfo = new JSONArray();
    }

    /**
     * @param iSql 类库
     */
    public ApproveProcess(IntegrateSql iSql) {
        super(iSql);
        vth = new FSysApprovingVth(iSql);
        vthSql = new TableBeanPrepareSql(vth);
        flowProcessInfo = new JSONArray();
    }

    /**
     * 获取指定环节办理人
     *
     * @param stepId 环节Id
     * @return 办理人列表
     */
    public List<String> getApproves(String stepId) {
        List<String> list = new ArrayList<>();
        try {
            vthSql.setSelectPrepare("KeyUnid=? and StepId=?");
            vthSql.setPstValue(1, this.getKeyUnid());
            vthSql.setPstValue(2, stepId);
            ResultSet rs = vthSql.executeQuery();
            while (rs.next()) {
                list.add(rs.getString("Approver"));
            }
            rs.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return list;
    }

    /**
     * 获取下一环节办理人
     *
     * @param toStepId 环节Id
     * @return 办理人列表
     */
    public JSONArray getNextApproves(String toStepId) {
        JSONArray arr = new JSONArray();
        try {
            StringBuffer sql = new StringBuffer(50);
            sql.append("select vth.Approver as id,p.PersonName as name,vth.Authorizer as auth from F_Sys_ApprovingVth vth inner join F_Sys_Personnel p on vth.Approver=p.PersonNumber ");
            sql.append(" where vth.KeyUnid=? and vth.StepId=? and vth.status=?");
            vthSql.setPrepare(sql);
            vthSql.setPstValue(1, this.getKeyUnid());
            vthSql.setPstValue(2, toStepId);
            vthSql.setPstValue(3, STATUS_IND);
            ResultSet rs = vthSql.executeQuery();
            arr = JsonGenerator.toJsonArray(rs);
//            System.out.println("arr = " + arr);
            rs.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return arr;
    }


    /**
     * 检查指定环节办理人是否存在。
     *
     * @param step   环节
     * @param userId user id
     * @return true 存在
     */
    public boolean isStepApproves(Step step, String userId, int status) {
        boolean b = false;
        try {

            vthSql.setSelectPrepare("KeyUnid=? and StepId=? and Approver=? and Status=?");
            vthSql.setPstValue(1, this.getKeyUnid());
            vthSql.setPstValue(2, step.getStepId());
            vthSql.setPstValue(3, userId);
            vthSql.setPstValue(4, status);
            ResultSet rs = vthSql.executeQuery();
            b = rs.next();
            rs.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return b;
    }

    /**
     * 获取办理人数量
     *
     * @return long
     */
    public long getStepApprovesNum() {
        long c = 0;
        try {
            String buf = "KeyUnid='" + this.getKeyUnid() + "'" + "and StepId='" +
                    this.getStepId() + "'" +
                    "and Status= " + STATUS_IND;
            c = this.getIntegrateSql().selectCount(vth.getTableName(), buf);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return c;
    }

    /**
     * 获取子流程未审完数量
     *
     * @return int 
     */
    public int getFlowSubApprovesNum() {
        int c = 0, i = 1;
        try {
            tSql = new TableBeanPrepareSql(this);
            StringBuffer sql = new StringBuffer(50);
            sql.append("select count(KeyUnid) as tt from ").append(this.getTableName());
            sql.append(" where ParentId=? and ParentStepId=? and (FlowStatus=? or FlowStatus=?)");
            tSql.setPrepare(sql);
            tSql.setPstValue(i++, this.getKeyUnid());
            tSql.setPstValue(i++, this.getStepId());
            tSql.setPstValue(i++, FlowStatusEnum.ING.getId());
            tSql.setPstValue(i, FlowStatusEnum.START.getId());
//            System.out.println("sql = " + sql);
//            System.out.println("this.getKeyUnid() = " + this.getKeyUnid());
//            System.out.println("this.getStepId() = " + this.getStepId());

            ResultSet rs = tSql.executeQuery();
            if (rs.next()) {
                c = rs.getInt(1);
//                System.out.println("---->YYYYYYYYYYYY:" + c);
//            }else{
//                System.out.println("---->XXXXXXXXXXXXX");
            }
            rs.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
        return c;
    }

    /**
     * 检查当前环节办理人是否存在。
     *
     * @param user user id
     * @return true 存在
     */
    public boolean isStepApproves(String user, int status) throws Exception {
        return this.isStepApproves(this.getStep(), user, status);
    }

    /**
     * 删除环节下所有的办理人
     *
     * @param step 环节
     */
    public void delStepAllApproves(Step step) throws SQLException {
        vthSql.setDeletePrepare("keyUnid=? and StepId=?");
        vthSql.setPstValue(1, this.getKeyUnid());
        vthSql.setPstValue(2, step.getStepId());
        vthSql.executeUpdate();
    }

    /**
     * 删除审批中的办理人
     *
     * @param step 环节
     * @throws Exception 错误
     */
    public void delStepApproving(Step step) throws Exception {
        vthSql.setDeletePrepare("keyUnid=? and StepId=? and Status=?");
        vthSql.setPstValue(1, this.getKeyUnid());
        vthSql.setPstValue(2, step.getStepId());
        vthSql.setPstValue(3, STATUS_IND);
        vthSql.executeUpdate();
    }

    /**
     * 退回标识。
     */
    public boolean isReturned(){
        return this.getIsReturned() == 1;
    }
    /**
     * 替换办理人。
     *
     * @param step 环节
     * @param Approver 办理人
     * @param actionUserId 操作用户Id
     * @throws Exception 错误
     */
    public void replaceApprover(Step step, String Approver, String actionUserId) throws Exception {
        vthSql.setUpdatePrepare("Approver=?,SysEditTime=?", "keyUnid=? and StepId=? and Approver=?");
        vthSql.setPstValue(1, Approver);
        vthSql.setPstValue(2, Pl.getCurDataTime());
        vthSql.setPstValue(3, this.getKeyUnid());
        vthSql.setPstValue(4, step.getStepId());
        vthSql.setPstValue(5, actionUserId);
        vthSql.executeUpdate();
        //TODO 转审的是否需要处理授权
    }

//    /**
//     * 更新环节的办理人状态为完成，办理意见
//     *
//     * @param step    当前环节
//     * @param userId  办理人Id
//     * @param options 办理意见
//     * @throws SQLException
//     */
//    public void updateApproverToEnd(Step step, String userId, String options) throws Exception {
//        updateApproverToStatus(step.getStepId(), userId, options, 1, STATUS_END);
//    }

    /**
     * 更新环节的办理人状态为完成，办理意见，如果是会审或组织会审，则更新结果字段。
     *
     * @param step     当前环节
     * @param userId   办理人Id
     * @param options  办理意见
     * @param hsResult 会审结果(0 不通过，1 通过)
     * @throws Exception 错误
     */
    public void updateApproverToEnd(Step step, String userId, String options, int hsResult) throws Exception {
        updateApproverToStatus(step.getStepId(), userId, options, hsResult, STATUS_END);
    }

    /**
     * @param stepId
     * @param userId   办理人Id
     * @param options  办理意见
     * @param hsResult 会审结果(0 不通过，1 通过)
     * @throws Exception 错误
     */
    public void updateApproverToEnd(String stepId, String userId, String options, int hsResult) throws Exception {
        updateApproverToStatus(stepId, userId, options, hsResult, STATUS_END);
    }

    /**
     * 更新环节的办理人状态为暂停
     *
     * @param step 环节
     * @param userId 用户Id
     * @param options 办理意见
     * @throws Exception 抛出错误
     */
    public void updateApproverToPause(Step step, String userId, String options) throws Exception {
        this.updateApproverToStatus(step.getStepId(), userId, options, 1, STATUS_PAUSE);
    }

    /**
     * @param stepId 环节Id
     * @param userId 用户Id
     * @param options 办理意见
     * @param hsResult 会审结果
     * @param status 状态
     * @throws Exception 抛出错误
     */
    private void updateApproverToStatus(String stepId, String userId, String options, int hsResult, int status) throws Exception {
        int i = 1;
        vthSql.setUpdatePrepare("status=?,Options=?,HSResult=?,SysEditTime=?", "keyUnid=? and StepId=? and Approver=?");
        vthSql.setPstValue(i++, status);
        vthSql.setPstValue(i++, options);
        vthSql.setPstValue(i++, hsResult);
        vthSql.setPstValue(i++, Pl.getCurDataTime());
        vthSql.setPstValue(i++, getKeyUnid());
        vthSql.setPstValue(i++, stepId);
        vthSql.setPstValue(i, userId);
        vthSql.executeUpdate();
    }

    /**
     * 停止办理人
     *
     * @param stepId 环节Id
     * @throws Exception 抛出错误
     */
    public void updateApproverIngToStop(String stepId) throws Exception {
        int i = 1;
        vthSql.setUpdatePrepare("status=?,SysEditTime=?", "keyUnid=? and StepId=? and Status=?");
        vthSql.setPstValue(i++, STATUS_STOP);
        vthSql.setPstValue(i++, Pl.getCurDataTime());
        vthSql.setPstValue(i++, getKeyUnid());
        vthSql.setPstValue(i++, stepId);
        vthSql.setPstValue(i, STATUS_IND);
        vthSql.executeUpdate();
    }

    /**
     * @param step 环节
     * @throws Exception 抛出错误
     */
    public String getPauseApprover(Step step) throws Exception {
        int i = 1;
        String approver;
        vthSql.setSelectPrepare("keyUnid=? and StepId=? and Status=?");
        vthSql.setPstValue(i++, getKeyUnid());
        vthSql.setPstValue(i++, step.getStepId());
        vthSql.setPstValue(i, STATUS_PAUSE);
        ResultSet rs = vthSql.executeQuery();
        if (rs.next()) {
            approver = rs.getString("Approver");
        } else {
            throw new FlowException(ErrorCodeEnum.PAUSEAPPROVER_NOFIND_ERROR);
        }
        rs.close();
        return approver;
    }

    /**
     * 用户id+appKeyId 是查找是否有
     *
     * @param approvers 办理人数组
     */
    private HashMap<String, String> handleAuthorize(String[] approvers) throws Exception {
        Authorize authorize = new Authorize();
        return authorize.getAuthorized(getIntegrateSql(), approvers, this.getAppKeyId());
    }

    /**
     * 添加办理人
     *
     * @param approver userId
     * @throws Exception 抛出错误
     */
    public void addToStepApprover(String approver) throws Exception {
        this.addToStepApprover(approver.split(";"));
    }

    /**
     * 添加办理人
     *
     * @param approver userId
     * @throws Exception 抛出错误
     */
    public Map<String, String> addToStepApprover(String[] approver) throws Exception {
        if (approver == null || approver.length == 0) {
            throw new FlowException(ErrorCodeEnum.APPROVER_NOFIND_ERROR);
        }
        //处理办理人中是否有授权。
        Map<String, String> map = handleAuthorize(approver);
        addApprovers(map);
        return map;
    }

//    /**
//     * 添加办理人
//     *
//     * @param map 当前人
//     * @throws Exception
//     */
//    public void addToStepApprover(Map<String, String> map) throws Exception {
//        addApprovers(map);
//    }
//
//    /**
//     * 把办理人写到主文档记录中。
//     *
//     * @param subKeyUnid 子流程的KeyUnid
//     * @throws Exception 抛出错误
//     */
//    public void addStepApproverToMain(String subKeyUnid) throws Exception {
//        StringBuilder sql = new StringBuilder(100);
//        sql.append(vth.selectCode()).append("where KeyUnid='").append(subKeyUnid).append("'");
//        ResultSet rs = integrateSql.executeQuery(sql.toString());
//
//        if (rs.next()) {
//            vth.setInsert();
//            do {
//                vth.initParamToRs(rs);
//                vth.setKeyUnid(this.getKeyUnid()); //替换成主文档的
//                vth.setStepId(this.getStepId());
//                vth.addBatch();
//            } while (rs.next());
//            vth.executeBatch();
//        }
//    }

    /**
     * 
     * @param map 办理人
     * @throws Exception 错误
     */
    private void addApprovers(Map<String, String> map) throws Exception {
        vth.setInsert();
        vth.setSysId(this.getSysId()); //租户ID
        vth.setKeyUnid(this.getKeyUnid()); //单据关联
        vth.setStepId(this.getStepId()); //环节Id
        vth.setSysAddTime(Pl.getCurDataTime());
        vth.setSysEditTime(vth.getSysAddTime());
        vth.setStatus(STATUS_IND); //状态

        for (Map.Entry<String, String> entry : map.entrySet()) {
            vth.setApprover(entry.getKey()); //办理人Id
            vth.setAuthorizer(entry.getValue());
            vth.addBatch();
//            System.out.println("下一环节办理人：" + entry.getKey());
        }



        vth.executeBatch();
    }

    /**
     * 初始化子流程
     *
     * @param step 环节
     * @throws Exception 抛出错误
     */
    public void initAllFlowSub(Step step) throws Exception {
//        approveFlowSub = new FSysApproveFlowSub(this.integrateSql);
        tSql = new TableBeanPrepareSql(this);
        tSql.setDeletePrepare("ParentId=? and ParentStepId=?");
        tSql.setPstValue(1, this.getKeyUnid());
        tSql.setPstValue(2, step.getStepId());
        tSql.executeUpdate();
    }

    /**
     * 获取已审批的会审结果
     *
     * @return 会审结果
     * @throws Exception 抛出错误
     */
    public int getHSResult() throws Exception {
        int isOk = HSRESULT_ING;
        int yes = 0, no = 0, count = 0;
        String hsway = this.getHSWay();

        if (hsway.equals("")) {
            throw new FlowException(ErrorCodeEnum.HSWay_FIELD);
        }
        vthSql.setSelectPrepare(" KeyUnid=? and StepId=? ");
        vthSql.setPstValue(1, this.getKeyUnid());
        vthSql.setPstValue(2, this.getStepId());
        ResultSet rs = vthSql.executeQuery();
        if (rs.next()) {
            String[] way = hsway.split(";");
            do {
                count++;
                if (rs.getInt("Status") == STATUS_END) {
                    if (rs.getInt("HSResult") == SubmitFlowImpl.HSResult_YES) {
                        yes++;
                    } else {
                        no++;
                    }

                    if (way[0].equals(HSWayEnum.KEY.getKey())) { //关键路径
                        if (way[2].equals(rs.getString("Approver"))) {
                            isOk = rs.getInt("HSResult");
                            break;
                        }
                    }
                }
            } while (rs.next());

//            System.out.println("no = " + no);
//            System.out.println("yes = " + yes);
//            System.out.println("way[0] = " + way[0]);
            HSWayEnum hswayType = HSWayEnum.getHSWayByValue(way[0]);
            switch (hswayType) {
                case ALL:
                    if (no > 0) {
                        isOk = HSRESULT_NO;
                    } else if (yes == count) {
                        isOk = HSRESULT_YES;
                    }
                    break;
                case NUM:
                    int ww = Integer.parseInt(way[1]);
                    if (yes >= ww) {
                        isOk = HSRESULT_YES;
                    } else if (count - yes - no < ww) {
                        isOk = HSRESULT_NO;
                    }
                    break;
                case MOST:
                    if (yes > count - yes - no) {
                        isOk = HSRESULT_YES;
                    } else if (no >= count - yes - no) {
                        isOk = HSRESULT_NO;
                    }
                    break;
            }
        }
        rs.close();

        return isOk;
    }

    /**
     * 获取当前环节
     * @return 环节
     * @throws Exception 抛出错误
     */
    public Step getStep() throws Exception {
        if (curStep == null) {
            curStep = new Step(this.getIntegrateSql(), this.getStepId());
            if (!curStep.isHas()) {
                throw new FlowException(ErrorCodeEnum.FORMSTEP_NOFIND_ERROR);
            }
        }
        return curStep;
    }

    /**
     * 把审批中的数据迁到审批完成。
     *
     * @throws Exception 抛出错误
     */
    public void insertToEnd() throws Exception {
        this.integrateSql.executeUpdate("insert into " + vth.getTableName() + "ed select * from " + vth.getTableName() + " where keyUnid='" + this.getKeyUnid() + "'");
        this.integrateSql.deleteData(vth.getTableName(), this.getKeyName(), this.getKeyUnid());

        this.integrateSql.executeUpdate("insert into " + this.getTableName() + "ed select * from " + this.getTableName() + " where keyUnid='" + this.getKeyUnid() + "'");
        this.integrateSql.deleteData(this.getTableName(), this.getKeyName(), this.getKeyUnid());
    }

    /**
     * 添加输出的结构
     *
     * @param flow 流程
     * @param step 环节
     * @param process 办理数据
     */
    public void appendFlowProcessInfo(Flow flow, Step step, ApproveProcess process) {
        //System.out.println("appendFlowProcessInfo = " + step.getStepId());
        JSONObject json = new JSONObject();
        json.put("flowId", flow.getFlowId());
        json.put("flowName", flow.getFlowName());
        json.put("stepId", step.getStepId());
        json.put("stepName", step.getStepName());
        json.put("keyUnid", process.getKeyUnid());
        json.put("nextApproves", process.getNextApproves(step.getStepId()));
        flowProcessInfo.add(json);
    }

    /**
     * 添加下一条子流程，用子流程提交
     *
     * @throws Exception 错误
     */
    public void appendFlowProcessInfoNext() throws Exception {
        StringBuilder sql = new StringBuilder(100);
        sql.append("select KeyUnid from ").append(this.getTableName()).append(" where ParentId='").append(this.getParentId()).append("'")
                .append(" and (FlowStatus=").append(FlowStatusEnum.START.getId()).append(" or FlowStatus=").append(FlowStatusEnum.ING.getId()).append(")");
        ResultSet rs = this.integrateSql.executeQuery(sql.toString());
        //System.out.println("sql = " + sql);
        if (rs.next()) {
            //System.out.println("KeyUnid = " + rs.getString("KeyUnid"));
            ApproveProcess process = new ApproveProcess(this.getIntegrateSql(), rs.getString("KeyUnid"));
            JSONObject json = new JSONObject();
            json.put("flowId", process.getFlowId());
            json.put("flowName", "");
            json.put("stepId", process.getStepId());
            json.put("stepName", "");
            json.put("keyUnid", rs.getString("KeyUnid"));
            json.put("nextApproves", process.getNextApproves(process.getStepId()));
            process.close();
            flowProcessInfo.add(json);
        }
        rs.close();
    }

    /**
     * 添加返回办理人 
     * [{"stepName":"xx","keyUnid":"xx","nextApproves":[{"auth":"","name":"xx","id":"xx"}],"stepId":"xx","flowId":"xx","flowName":"xx"}]
     * @param flow 流程
     * @param step 环节
     */
    public void appendFlowProcessInfo(Flow flow, Step step) {
        this.appendFlowProcessInfo(flow, step, this);
    }

    /**
     * 关闭
     */
    public void close() {
        try {
            if (curStep != null) curStep.close();
            if (tSql != null) tSql.close();
            vthSql.close();
            vth.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
